//==============================================================================
// Copyright (c) 2008-2013 Niall Ryan. All Rights Reserved.
//==============================================================================

#include <gtest/gtest.h>

#include "hydra/thread/Thread.h"
#include "hydra/vectors/LockFreeVector.h"

using namespace Hydra;

template<typename Vector>
class VectorTest : public ::testing::Test
{
protected:
	static const int NUM_THREADS = 4;
	static const int NUM_ITERATIONS = 1000000;
	static void Push(VectorTest* test, int threadId)
	{
		for( int i=0; i<NUM_ITERATIONS; ++i )
			test->m_vector.pushBack(threadId*NUM_ITERATIONS+i);
	}

	Vector m_vector;
};

typedef ::testing::Types<LockFreeVector<int> > VectorTestTypes;
TYPED_TEST_CASE(VectorTest, VectorTestTypes);

TYPED_TEST(VectorTest, Stuff)
{
	//TEST_TIMER;

	//
	//single threaded functionality tests
	//
	TypeParam testVector;
	EXPECT_TRUE(testVector.isEmpty());
	EXPECT_TRUE(testVector.getSize() == 0);
	testVector.pushBack(10);
	EXPECT_TRUE(!testVector.isEmpty());
	EXPECT_TRUE(testVector.getSize() == 1);
	EXPECT_TRUE(testVector[0] == 10);
	testVector[0] = 20;
	EXPECT_TRUE(testVector[0] == 20);
	testVector.clear();
	EXPECT_TRUE(testVector.isEmpty());
	EXPECT_TRUE(testVector.getSize() == 0);
	for( int i=0; i<100; ++i )
		testVector.pushBack(i+1000);
	EXPECT_TRUE(testVector.getSize() == 100);
	for( int i=0; i<100; ++i )
		EXPECT_TRUE(testVector[i] == i+1000);

	//
	//multithread tests
	//
	Thread threads[NUM_THREADS];
	{
		for( int i=0; i<NUM_THREADS; ++i )
			threads[i] = Thread(&Push, this, i);
		for( int i=0; i<NUM_THREADS; ++i )
			threads[i].Start();
		for( int i=0; i<NUM_THREADS; ++i )
			threads[i].Join();

		//verify vector contains the right values in the expected order
		EXPECT_TRUE(m_vector.getSize() == NUM_THREADS*NUM_ITERATIONS);
		int nextValue[NUM_THREADS];
		for( int i=0; i<NUM_THREADS; ++i )
			nextValue[i] = i*NUM_ITERATIONS;
		for( unsigned int vecIndex=0; vecIndex<m_vector.getSize(); ++vecIndex )
		{
			int value = m_vector[vecIndex];

			bool isFound = false;
			for( int i=0; i<NUM_THREADS; ++i )
			{
				if( nextValue[i] == value )
				{
					isFound = true;
					++nextValue[i];
					break;
				}
			}
			EXPECT_TRUE(isFound);
		}
	}
}

